Skip to content

fix(listen): restrict feature prompt to bare-invocation guided flow#68

Merged
lukeocodes merged 1 commit intomainfrom
fix/listen-any-arg-non-interactive
May 8, 2026
Merged

fix(listen): restrict feature prompt to bare-invocation guided flow#68
lukeocodes merged 1 commit intomainfrom
fix/listen-any-arg-non-interactive

Conversation

@lukeocodes
Copy link
Copy Markdown
Member

The bug

```
$ dg listen https://dpgr.am/spacewalk.wav | cat
Optional features (press Enter to skip all):
Speaker diarization [Speaker 0] / [Speaker 1] … [y/n] (n):
```

The moment the user provides any source/flag/value, they're scripting — they should never see the optional-features prompt. Today it fires whenever they didn't also pass one of `--diarize`/`--summarize`/`--topics`/`--sentiment`.

The fix

The interactive feature prompt belongs to the bare-`dg listen` guided flow only. Lift the `_interactive_features()` call into the same branch as `_interactive_select_source()`, kill the standalone gate, and anchor the check on a local `guided_flow` boolean that flips `True` only when the user invokes `dg listen` with nothing.

Behaviour matrix

Invocation Source select Feature prompt
`dg listen` ✅ asked ✅ asked
`dg listen URL` ❌ skipped ❌ skipped
`dg listen FILE` ❌ skipped ❌ skipped
`dg listen URL --diarize` ❌ skipped ❌ skipped
`dg listen --mic` ❌ skipped ❌ skipped
`dg listen URL | cat` ❌ skipped ❌ skipped
`echo audio | dg listen` ❌ skipped (stdin mode) ❌ skipped

Tests

5 new cases in `TestGuidedFlow`:

  • `test_url_arg_skips_both_prompts`
  • `test_file_arg_skips_both_prompts`
  • `test_url_arg_with_diarize_skips_both_prompts`
  • `test_bare_invocation_runs_full_guided_flow`
  • `test_cancelled_source_select_returns_cancelled`

All 16 listen tests pass: `uv run pytest packages/deepctl-cmd-listen/tests/unit/test_listen_command.py` → 16 passed in 0.79s.

Stack

This is PR-C of a three-PR sequence agreed in the analyse-mode review:

  • C (this PR) — `dg listen` 'any arg = non-interactive'
  • B — flag restructure: rename current `--agent-friendly` (metadata + exit) → `--introspect`; redefine `--agent-friendly` global = non-interactive + JSON; add `--json` global alias
  • A — argv-injection bootstrap: soft signals prepend `--agent-friendly` to `sys.argv`, `is_agentic()` collapses to a pure argv check

The optional-features prompt (Speaker diarization / Generate summary /
Detect topics / Sentiment analysis) was firing whenever the user passed
a source as a positional arg without also passing a feature flag. So:

  dg listen URL                           # prompted
  dg listen URL | cat                     # also prompted (only +1 of 3
                                          #  soft signals — score below
                                          #  threshold so _agentic stayed
                                          #  False)
  dg listen URL --diarize                 # finally skipped

Per the new mental model: the moment the user provides any arg, value,
or flag, they have signalled scripting intent. The interactive flow
exists only for bare 'dg listen' invocations.

This commit lifts the _interactive_features() call into the bare-
invocation branch where _interactive_select_source() lives, and removes
the standalone gate that was triggering on its own. A new local
guided_flow boolean (initialised False, flipped True only in the bare-
invocation else-branch) anchors the check at the call site.

After this:

  dg listen URL                           # no prompts
  dg listen URL --diarize                 # no prompts (already worked)
  dg listen URL | cat                     # no prompts
  dg listen --mic                         # no prompts
  dg listen                               # full guided flow (source +
                                          #  features, as before)

Five new pytest cases in TestGuidedFlow lock the invariants: URL arg
skips both prompts, file arg skips both, URL+--diarize skips both,
bare invocation runs both, and cancelled source-select returns the
cancelled BaseResult without ever asking about features.
@lukeocodes lukeocodes merged commit ee92d1f into main May 8, 2026
38 checks passed
@lukeocodes lukeocodes deleted the fix/listen-any-arg-non-interactive branch May 8, 2026 19:14
@github-actions github-actions Bot mentioned this pull request May 8, 2026
lukeocodes added a commit that referenced this pull request May 8, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>0.2.22</summary>

## [0.2.22](v0.2.21...v0.2.22)
(2026-05-08)


### Bug Fixes

* **listen:** restrict feature prompt to bare-invocation guided flow
([6ea275b](6ea275b))
* **listen:** restrict feature prompt to bare-invocation guided flow
([#68](#68))
([ee92d1f](ee92d1f))
* **non-interactive:** promote CI to hard signal, add --non-interactive
flag
([8da4e21](8da4e21))
* **non-interactive:** promote CI=1 to hard signal, add
--non-interactive flag
([#67](#67))
([a25099d](a25099d))
</details>

<details><summary>deepctl-core: 0.2.11</summary>

##
[0.2.11](deepctl-core-v0.2.10...deepctl-core-v0.2.11)
(2026-05-08)


### Bug Fixes

* **non-interactive:** promote CI to hard signal, add --non-interactive
flag
([8da4e21](8da4e21))
* **non-interactive:** promote CI=1 to hard signal, add
--non-interactive flag
([#67](#67))
([a25099d](a25099d))
</details>

<details><summary>deepctl-cmd-listen: 0.0.12</summary>

##
[0.0.12](deepctl-cmd-listen-v0.0.11...deepctl-cmd-listen-v0.0.12)
(2026-05-08)


### Bug Fixes

* **listen:** restrict feature prompt to bare-invocation guided flow
([6ea275b](6ea275b))
* **listen:** restrict feature prompt to bare-invocation guided flow
([#68](#68))
([ee92d1f](ee92d1f))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
lukeocodes added a commit that referenced this pull request May 9, 2026
## What

PR #68 fixed the "any arg = non-interactive" rule for `dg listen` only.
This PR extends the same rule uniformly to **every** command that
prompts.

## How

New `BaseCommand.is_guided(ctx)` helper — returns `True` only when the
user invoked with zero input (no positional, no flag, no env var, not in
agentic mode). `BaseCommand.execute` sets `self._guided` before calling
`handle()`.

Threaded through one seam plus three call-site fixes:

| Surface | Change |
|---|---|
| `BaseCommand.confirm` / `BaseCommand.prompt` | Now respect `_guided`
in addition to `_agentic` / `ci_friendly`. Every command using these
helpers gets the rule for free. |
| `dg login` `_maybe_prompt_skills_setup` | Bails on either non-tty OR
not-guided. (The other two login prompts are downstream of
`self.confirm` and inherit the gate.) |
| `dg skills` | 3 direct `click.confirm` / `click.prompt` calls migrated
to `self.confirm`. The `is_tty` skill-list prompt also gates on
`_guided` (existing 'non-TTY without --all installs all' fallback still
active). |
| `dg debug browser` | "Press Enter to open the debugger" prompt skipped
when not guided. Browser still opens automatically. (Per Luke: "should
have a flag to instantly open it" — `--non-interactive` is already that
flag.) |
| `dg listen` | **Unchanged.** Its local `guided_flow` boolean captures
listen-specific intent more precisely than the generic `_guided` (was
`_interactive_select_source` actually run?). Tests untouched. |

## Tests

```
4 passed (TestIsGuided)
```

- `test_bare_invocation_is_guided` — all params from DEFAULT/DEFAULT_MAP
→ guided
- `test_any_commandline_arg_breaks_guided` — one COMMANDLINE source
breaks it
- `test_env_var_breaks_guided` — ENVIRONMENT source also breaks it
- `test_agentic_short_circuits_to_false` — `_agentic=True` always
returns False

The 5 pre-existing `test_output_result_*` failures in `TestBaseCommand`
are unrelated and present on `main` (flagged in #74's body).

## Behaviour matrix

| Invocation | Outcome |
|---|---|
| `dg login` (bare) | guided login flow as before |
| `dg login --api-key X` | non-interactive: skips skills-setup prompt,
no profile-name prompt |
| `dg skills install` (bare in TTY) | shows numbered list + asks |
| `dg skills install --all` | installs all silently |
| `dg skills install --tool claude --non-interactive` | installs for
claude, no prompts |
| `dg debug browser` (bare) | press-Enter, then opens |
| `dg debug browser --port 9000` | opens immediately at port 9000 |
| `dg debug browser --non-interactive` | opens immediately |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant